package com.zoe.onelink.redis.registry;

import com.zoe.onelink.redis.enumeration.RedisTemplateValueType;
import com.zoe.onelink.redis.property.RedisMultiDatasourceProperties;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.Map;

/**
 * <p>标题: Redis多数据源支持</p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2021</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2021-09-11
 */
@RequiredArgsConstructor
@Slf4j
public class RedisMultiDatasourceRegistry implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {


    private Environment environment;

    @SneakyThrows
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {

        // 获取多数据源配置
        RedisMultiDatasourceProperties redisMultiDatasourceProperties = Binder.get(environment)
                .bindOrCreate(RedisMultiDatasourceProperties.PROPERTY_PREFIX, RedisMultiDatasourceProperties.class);

        // 数据源
        Map<String, RedisMultiDatasourceProperties.RedisMultiProperties> datasourceMap = redisMultiDatasourceProperties.getDatasourceMap();

        // 数据源数量
        if (datasourceMap.size() == 0) {
            return;
        }

        log.info("[Redis多数据源] 开始创建,数据源数量:{}", datasourceMap.size());

        // 创建数据源
        for (Map.Entry<String, RedisMultiDatasourceProperties.RedisMultiProperties> e : datasourceMap.entrySet()) {

            // 获取数据源配置信息
            RedisMultiDatasourceProperties.RedisMultiProperties redisMultiProperties = e.getValue();

            // 创建连接工厂
            String redisConnectionFactoryName = e.getKey() + "_REDIS_CONNECTION_FACTORY_BEAN_SUFFIX";

            // 定义 redisConnectionFactory 注册信息
            RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
            redisStandaloneConfiguration.setHostName(redisMultiProperties.getHost());
            redisStandaloneConfiguration.setPort(redisMultiProperties.getPort());
            redisStandaloneConfiguration.setPassword(RedisPassword.of(redisMultiProperties.getPassword()));
            redisStandaloneConfiguration.setDatabase(redisMultiProperties.getDatabase());
            AbstractBeanDefinition redisConnectionFactoryBeanDef = BeanDefinitionBuilder.rootBeanDefinition(LettuceConnectionFactory.class)
                    .addConstructorArgValue(redisStandaloneConfiguration)
                    .addConstructorArgValue(LettuceClientConfiguration.defaultConfiguration())
                    .getBeanDefinition();

            // 注册 redisConnectionFactory
            registry.registerBeanDefinition(redisConnectionFactoryName, redisConnectionFactoryBeanDef);

            // 定义 redisTemplate 工厂注册信息
            AbstractBeanDefinition redisTemplateBeanDef = BeanDefinitionBuilder.genericBeanDefinition()
                    .addPropertyReference("connectionFactory", redisConnectionFactoryName)
                    .addPropertyValue("keySerializer", redisMultiProperties.getKeySerializer().newInstance())
                    .addPropertyValue("valueSerializer", redisMultiProperties.getValueSerializer().newInstance())
                    .addPropertyValue("hashKeySerializer", redisMultiProperties.getHashKeySerializer().newInstance())
                    .addPropertyValue("hashValueSerializer", redisMultiProperties.getHashValueSerializer().newInstance())
                    .addDependsOn(redisConnectionFactoryName)
                    .getBeanDefinition();

            // 根据类型创建
            if (RedisTemplateValueType.STRING.equals(redisMultiProperties.getValueType())) {
                redisTemplateBeanDef.setBeanClass(StringRedisTemplate.class);
            } else {
                redisTemplateBeanDef.setBeanClass(RedisTemplate.class);
            }

            // 注册实例
            String redisTemplateBeanName = e.getKey();
            registry.registerBeanDefinition(redisTemplateBeanName, redisTemplateBeanDef);

            log.info("[Redis多数据源] Redis实例[{}][{}][{}]创建成功", redisTemplateBeanName, redisMultiProperties.getDatabase(), redisMultiProperties.getValueType());

        }

        log.info("[Redis多数据源] 创建完成");

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

}
